﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
using System.Security.Cryptography;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Net.NetworkInformation;
using System.Windows;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Reflection;
using JmShared;
using NLog;
using System.Security;
using Newtonsoft.Json;
using System.Management;


namespace JiveMessenger.Utilities
{
    public static class StaticMethods
    {

        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        public static string GetEnumDescription(Enum enumObj)
        {
            FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());

            object[] attribArray = fieldInfo.GetCustomAttributes(false);

            if (attribArray.Length == 0)
            {
                return enumObj.ToString();
            }
            else
            {
                EnumDescriptionAttribute attrib = attribArray[0] as EnumDescriptionAttribute;
                return attrib.Description;
            }
        }

        public static Boolean SecureStringEqual(SecureString secureString1, SecureString secureString2)
        {
            if (secureString1 == null)
            {
                throw new ArgumentNullException("s1");
            }

            if (secureString2 == null)
            {
                throw new ArgumentNullException("s2");
            }

            if (secureString1.Length != secureString2.Length)
            {
                return false;
            }

            IntPtr ss_bstr1_ptr = IntPtr.Zero;
            IntPtr ss_bstr2_ptr = IntPtr.Zero;

            try
            {
                ss_bstr1_ptr = Marshal.SecureStringToBSTR(secureString1);
                ss_bstr2_ptr = Marshal.SecureStringToBSTR(secureString2);

                String str1 = Marshal.PtrToStringBSTR(ss_bstr1_ptr);
                String str2 = Marshal.PtrToStringBSTR(ss_bstr2_ptr);

                return str1.Equals(str2);
            }
            finally
            {
                if (ss_bstr1_ptr != IntPtr.Zero)
                {
                    Marshal.ZeroFreeBSTR(ss_bstr1_ptr);
                }

                if (ss_bstr2_ptr != IntPtr.Zero)
                {
                    Marshal.ZeroFreeBSTR(ss_bstr2_ptr);
                }
            }
        }
        public static bool IsValidEmail(string emailString)
        {
            Boolean mappingInvalid = false;
            emailString = Regex.Replace(emailString, @"(@)(.+)$", match =>
            {
                String domainName = match.Groups[2].Value;
                try
                {
                    domainName = new IdnMapping().GetAscii(domainName);
                }
                catch (ArgumentException)
                {
                    mappingInvalid = true;
                }
                return match.Groups[1].Value + domainName;
            });
            if (mappingInvalid)
            {
                return false;
            }
            return Regex.IsMatch(emailString,
                    @"^(?("")(""[^""]+?""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
                    @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9]{2,17}))$",
                    RegexOptions.IgnoreCase);
        }


        public static string GetUniqueID()
        {

            string cpuInfo = string.Empty;
            ManagementClass mc = new ManagementClass("win32_processor");
            ManagementObjectCollection moc = mc.GetInstances();

            foreach (ManagementObject mo in moc)
            {
                cpuInfo = mo.Properties["processorID"].Value.ToString();
                break;
            }

            string drive = "C";
            ManagementObject dsk = new ManagementObject(
                @"win32_logicaldisk.deviceid=""" + drive + @":""");
            dsk.Get();
            string volumeSerial = dsk["VolumeSerialNumber"].ToString();

            string uniqueId = cpuInfo + volumeSerial;
            return uniqueId;
            //var result = new List<string>();
            //NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();

            //NetworkInterface adapter = interfaces.FirstOrDefault(x => x.OperationalStatus == OperationalStatus.Up);
            //if (adapter == null)
            //    adapter = interfaces.FirstOrDefault();
            //if (adapter == null)
            //    throw new InvalidOperationException("No network card found");

            //PhysicalAddress address = adapter.GetPhysicalAddress();
            //byte[] bytes = address.GetAddressBytes();
            //if (bytes.Length == 0)
            //    throw new InvalidOperationException("Network card has invalid mac address");
            //var mac = new StringBuilder();
            //for (int i = 0; i < bytes.Length; i++)
            //{
            //    mac.Append(bytes[i].ToString("X2"));
            //    if (i != bytes.Length - 1) mac.Append("-");
            //}
            //return mac.GetHashCode().ToString("x2");

            //foreach (NetworkInterface adapter in interfaces)
            //{
            //    PhysicalAddress address = adapter.GetPhysicalAddress();
            //    byte[] bytes = address.GetAddressBytes();
            //    if (bytes.Length == 0) continue;

            //    var mac = new StringBuilder();
            //    for (int i = 0; i < bytes.Length; i++)
            //    {
            //        mac.Append(bytes[i].ToString("X2"));
            //        if (i != bytes.Length - 1) mac.Append("-");
            //    }

            //    result.Add(mac.ToString());
            //}
            //return result.ToArray();
        }
        private static readonly char[] HexDigits = {
         '0', '1', '2', '3', '4', '5', '6', '7',
         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};



        public static string GenerateLogName(string loggedInUserName, string logName, string folderPath)
        {
            string validatedName = logName;
            int tries = 1;

            DirectoryInfo saveDir = new DirectoryInfo(CombinePath(folderPath, loggedInUserName));
            if (!saveDir.Exists)
                saveDir.Create();

            while (File.Exists(CombinePath(saveDir, Path.ChangeExtension(validatedName, ".xml"))))
            {
                FileInfo fi = new FileInfo(CombinePath(saveDir, Path.ChangeExtension(validatedName, ".xml")));
                if (fi.Length < 5000 * 1024)
                {
                    if (!File.Exists(CombinePath(saveDir, string.Format("{0} ({1}){2}", Path.GetFileNameWithoutExtension(logName), tries + 1, Path.GetExtension(logName)))))
                        return fi.FullName;
                }
                validatedName = string.Format("{0} ({1}){2}", Path.GetFileNameWithoutExtension(logName), tries++, Path.GetExtension(logName));
            }
            return CombinePath(saveDir, Path.ChangeExtension(validatedName, ".xml"));
        }

        public static int RandomNumber(int from, int to)
        {
            Random r = new Random();
            return r.Next(from, to);

        }
        public static string CombinePath(params object[] values)
        {
            string path = "";
            foreach (var str in values)
            {
                path += str.ToString().TrimStart(Path.DirectorySeparatorChar).EndsWith(Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture)) ? str : str.ToString() + Path.DirectorySeparatorChar;
            }

            return path.TrimEnd(Path.DirectorySeparatorChar);
        }

        /// <summary>
        /// Finds the visual parent.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sender">The sender.</param>
        /// <returns></returns>
        public static T FindVisualParent<T>(DependencyObject sender) where T : DependencyObject
        {
            if (sender == null)
            {
                return (null);
            }
            else if (VisualTreeHelper.GetParent(sender) is T)
            {
                return (VisualTreeHelper.GetParent(sender) as T);
            }
            else
            {
                DependencyObject parent = VisualTreeHelper.GetParent(sender);
                return (FindVisualParent<T>(parent));
            }
        }


        public static string ObjectToString<T>(T input)
        {
            var settings = new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.All,
                ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
                NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
                ObjectCreationHandling = ObjectCreationHandling.Auto,
                Formatting = Formatting.None
            };

            return Newtonsoft.Json.JsonConvert.SerializeObject(input, settings);
        }

        public static T StringToObject<T>(string input)
        {
            if (String.IsNullOrEmpty(input))
                return default(T);

            try
            {
                var settings = new JsonSerializerSettings()
                {
                    TypeNameHandling = TypeNameHandling.All,
                    ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
                    NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
                    ObjectCreationHandling = ObjectCreationHandling.Auto,
                    Formatting = Formatting.None
                };
                return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(input, settings);
            }
            catch { return default(T); }
        }

        public static string TimeSpanToFormattedString(int secs)
        {
            TimeSpan t = TimeSpan.FromSeconds(secs);
            string answer;
            if (t.TotalMinutes < 1.0)
            {
                answer = String.Format("{0}s", t.Seconds);
            }
            else if (t.TotalHours < 1.0)
            {
                answer = String.Format("{0}m{1:D2}s", t.Minutes, t.Seconds);
            }
            else // more than 1 hour
            {
                answer = String.Format("{0}h{1:D2}m{2:D2}s", (int)t.TotalHours, t.Minutes, t.Seconds);
            }

            return answer;
        }

        /// <summary>Returns true if the current application has focus, false otherwise</summary>
        public static bool ApplicationIsActivated()
        {
            var activatedHandle = GetForegroundWindow();
            if (activatedHandle == IntPtr.Zero)
            {
                return false;       // No window is currently activated
            }

            var procId = Process.GetCurrentProcess().Id;
            int activeProcId;
            GetWindowThreadProcessId(activatedHandle, out activeProcId);

            return activeProcId == procId;
        }

        public static string FileNameFriendly(string filename)
        {
            IList<char> invalidFileNameChars = Path.GetInvalidFileNameChars();
            Char[] result = filename.Select(ch => invalidFileNameChars.Contains(ch) ? Convert.ToChar(invalidFileNameChars.IndexOf(ch) + 65) : ch).ToArray();
            // Builds a string out of valid chars and replaces invalid chars with a unique letter
            var validFilename = new string(result);
            return validFilename;
        }

        public static string EncodePassword(string password)
        {
            using (SHA1 sha1 = SHA1.Create())
            {
                return string.Join("", sha1.ComputeHash(Encoding.UTF8.GetBytes(password)).Select(x => x.ToString("X2"))).ToLower();
            }
        }
        static byte[] entropy = System.Text.Encoding.Unicode.GetBytes("Random data 65__436vcxvnlkbgno9-4t548a5324gfdgfd");

        public static string EncryptString(System.Security.SecureString input)
        {
            byte[] encryptedData = System.Security.Cryptography.ProtectedData.Protect(
                System.Text.Encoding.Unicode.GetBytes(ToInsecureString(input)),
                entropy,
                System.Security.Cryptography.DataProtectionScope.CurrentUser);
            return Convert.ToBase64String(encryptedData);
        }

        public static SecureString DecryptString(string encryptedData)
        {
            if (string.IsNullOrEmpty(encryptedData))
                return new SecureString();
            try
            {
                byte[] decryptedData = System.Security.Cryptography.ProtectedData.Unprotect(
                    Convert.FromBase64String(encryptedData),
                    entropy,
                    System.Security.Cryptography.DataProtectionScope.CurrentUser);
                return ToSecureString(System.Text.Encoding.Unicode.GetString(decryptedData));
            }
            catch
            {
                return new SecureString();
            }
        }

        public static SecureString ToSecureString(string input)
        {
            SecureString secure = new SecureString();
            foreach (char c in input)
            {
                secure.AppendChar(c);
            }
            secure.MakeReadOnly();
            return secure;
        }

        public static string ToInsecureString(SecureString input)
        {
            string returnValue = string.Empty;
            IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(input);
            try
            {
                returnValue = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr);
            }
            finally
            {
                System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr);
            }
            return returnValue;
        }

        public static byte[] LoadImageData(string filePath)
        {

            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                using (BinaryReader br = new BinaryReader(fs))
                {

                    byte[] imageBytes = br.ReadBytes((int)fs.Length);

                    return imageBytes;
                }
            }
        }



        public static void SaveImageData(byte[] imageData, string filePath)
        {

            using (FileStream fs = StaticMethods.WaitForFile(filePath, FileMode.OpenOrCreate, FileAccess.Write, 1000))
            {
                if (fs == null)
                {
                    throw new IOException("File is in use: " + filePath);
                }

                using (BinaryWriter bw = new BinaryWriter(fs))
                {

                    bw.Write(imageData);

                }
            }

        }

        public static BitmapImage BitmapFromUri(Uri source)
        {
            var bitmap = new BitmapImage();

            try
            {
                bitmap.BeginInit();
                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                //bitmap.CreateOptions = BitmapCreateOptions.None;
                bitmap.UriSource = source;
                bitmap.EndInit();
                bitmap.Freeze();
            }
            catch (Exception err)
            {
                bitmap = null;
                Logger.Error("Error loading image source: " + source.AbsolutePath + ", " + err.Message);

            }

            return bitmap;
        }

        public static BitmapImage ByteArrayToBitmap(byte[] array)
        {
            if (array == null || array.Length == 0) return null;
            var image = new BitmapImage();
            using (var mem = new MemoryStream(array))
            {
                mem.Position = 0;
                image.BeginInit();
                image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
                image.CacheOption = BitmapCacheOption.OnLoad;
                image.UriSource = null;
                image.StreamSource = mem;
                image.EndInit();
            }
            image.Freeze();
            return image;
        }
        public static Byte[] BitmapToByteArray(BitmapImage imageSource)
        {
            Stream stream = imageSource.StreamSource;
            Byte[] buffer = null;
            if (stream != null && stream.Length > 0)
            {
                using (BinaryReader br = new BinaryReader(stream))
                {
                    buffer = br.ReadBytes((Int32)stream.Length);
                }
            }
            return buffer;
        }

        /// <summary>
        /// Attempts to grab a lock for a file stream, returns null if file is in use.
        /// </summary>
        /// <param name="fullPath"></param>
        /// <param name="mode"></param>
        /// <param name="access"></param>
        /// <returns></returns>
        public static FileStream WaitForFile(string fullPath, FileMode mode, FileAccess access, int timeOutInMilliseconds)
        {
            if (!File.Exists(fullPath) && (mode == FileMode.Open || mode == FileMode.Truncate || mode == FileMode.Append))
                throw new FileNotFoundException(fullPath);
            int numTries = 0;
            while (true)
            {
                ++numTries;
                try
                {
                    // Attempt to open the file exclusively.
                    FileStream fs = new FileStream(fullPath,
                        mode, access,
                        FileShare.None, 100);

                    //  fs.ReadByte();

                    // If we got this far the file is ready
                    return fs;

                }
                catch (Exception err)
                {

                    if (numTries > timeOutInMilliseconds / 10)
                    {
                        Logger.ErrorException("Timed out waiting for file lock", err);
                        return null;
                    }

                    // Wait for the lock to be released
                    System.Threading.Thread.Sleep(10);
                }
            }

        }

        public static T ConvertFromString<T>(string input)
        {
            if (String.IsNullOrEmpty(input))
                return default(T);
            TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
            return (T)converter.ConvertFromInvariantString(input);
        }
        public static string ConvertToString<T>(T input)
        {
            TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
            return (string)converter.ConvertToInvariantString(input);
        }
        public static SolidColorBrush HexStringToFrozenBrush(string hex)
        {
            SolidColorBrush b = new SolidColorBrush(HexStringToColor(hex));
            if (b.CanFreeze)
                b.Freeze();
            return b;
        }
        public static Color HexStringToColor(String hex)
        {

            try
            {
                if (String.IsNullOrEmpty(hex))
                    return Colors.Black;
                //remove the # at the front
                hex = hex.Replace("#", "");

                byte a = 255;
                byte r = 255;
                byte g = 255;
                byte b = 255;

                int start = 0;

                //handle ARGB strings (8 characters long)
                if (hex.Length == 8)
                {
                    a = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
                    start = 2;
                }

                //convert RGB characters to bytes
                r = byte.Parse(hex.Substring(start, 2), System.Globalization.NumberStyles.HexNumber);
                g = byte.Parse(hex.Substring(start + 2, 2), System.Globalization.NumberStyles.HexNumber);
                b = byte.Parse(hex.Substring(start + 4, 2), System.Globalization.NumberStyles.HexNumber);

                var color = Color.FromArgb(a, r, g, b);
                return color;
            }
            catch (Exception err)
            {
                Logger.ErrorException("Error parsing color", err);
                return Colors.Black;
            }
        }

        public static string ColorToHexString(Color color)
        {



            byte[] bytes = new byte[3];
            bytes[0] = color.R;
            bytes[1] = color.G;
            bytes[2] = color.B;
            char[] chars = new char[bytes.Length * 2];
            for (int i = 0; i < bytes.Length; i++)
            {
                int b = bytes[i];
                chars[i * 2] = HexDigits[b >> 4];
                chars[i * 2 + 1] = HexDigits[b & 0xF];
            }
            return new string(chars);
        }
        public static string ColorToHexString(string colorName)
        {
            if (String.IsNullOrEmpty(colorName))
                return null;
            SolidColorBrush col = (SolidColorBrush)new BrushConverter().ConvertFromString(colorName);
            if (col == null) return null;

            Color color = col.Color;
            return ColorToHexString(color);
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);

    }
}
